package com.fivec.javaproject.address;

import com.fivec.javaproject.address.utils.Base58;
import com.fivec.javaproject.address.utils.Hash;
import com.fivec.javaproject.generatebitaddress.code.tools.Base58Util;
import com.fivec.javaproject.generatebitaddress.code.tools.StaticMethod;

import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECPoint;
import java.util.Arrays;

/**
 * @author wangyizhu
 * @version "1.8.0_131"
 * @email 2641956765@qq.com
 * @desc:该类用于推导原始格式的比特币地址
 * @Time 2021/12/20 14:28
 */
public class Address {
    /**
     * 该方法用于生成一对随机的密钥并将生成的密钥对返回
     *
     * @return 生成的密钥对对象
     */
    public KeyPair generateKeys() {
        try {
            KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
            //secp256k1,secp256r1
            ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256k1");
            generator.initialize(ecSpec);
            return generator.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 该方法用于根据给定的密钥数据，推导生成对应的原始格式的比特币地址
     *
     * @param keyPair
     * @return 推导生成的比特币地址
     */
    public String newAddress(KeyPair keyPair) {
        ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
        ECPoint point = publicKey.getW();
        //原始格式的公钥
        byte[] pubKey = toUncompressPubKey(point);
        byte[] pubKeyHash256 = Hash.SHA256(pubKey);
        byte[] pubHash = Hash.RIPEMD160(pubKeyHash256);
        byte[] version = new byte[1];
        version[0] = 0x00;

        byte[] ver_pubHash = new byte[version.length + pubHash.length];
        System.arraycopy(version, 0, ver_pubHash, 0, version.length);
        System.arraycopy(pubHash, 0, ver_pubHash, 1, pubHash.length);

        //双哈希，取前4个字节
        byte[] hash1 = Hash.SHA256(ver_pubHash);
        byte[] hash2 = Hash.SHA256(hash1);
        byte[] check = new byte[4];
        System.arraycopy(hash2, 0, check, 0, check.length);

        byte[] ver_pubHash_Check = new byte[ver_pubHash.length + check.length];

        System.arraycopy(ver_pubHash, 0, ver_pubHash_Check, 0, ver_pubHash.length);
        System.arraycopy(check, 0, ver_pubHash_Check, ver_pubHash.length, check.length);
        //对拼接到的字节数组进行Base58
        return Base58.encode(ver_pubHash_Check);
    }

    /**
     * 该方法用于生成非压缩格式的公钥
     *
     * @return 非压缩格式的公钥
     */
    public static byte[] toUncompressPubKey(ECPoint point) {
        byte[] xBytes = point.getAffineX().toByteArray();
        byte[] yBytes = point.getAffineX().toByteArray();
        //原始格式 "04"+x坐标+y坐标=65字节
        byte[] pubKey = new byte[65];
        pubKey[0] = 04;
        if (xBytes.length == 32) {
            System.arraycopy(xBytes, 0, pubKey, 1, xBytes.length);
        } else if (xBytes.length > 32) {
            System.arraycopy(xBytes, 1, pubKey, 1, 32);
        }

        if (yBytes.length == 32) {
            System.arraycopy(xBytes, 0, pubKey, 33, yBytes.length);
        } else if (xBytes.length > 32) {
            System.arraycopy(xBytes, 1, pubKey, 33, 32);
        }
        return pubKey;
    }

    /**
     * 校验比特币地址是否符合规范，返回校验结果
     *
     * @param address
     * @return true地址有效  false地址无效
     */
    public  boolean Isvalidforadress(String address) {
        //将地址进行base58反编码，生成的其实是version+Pub Key hash+ checksum这25个字节
        byte[] version_public_checksumBytes = Base58Util.decode(address);
        int len = version_public_checksumBytes.length;
        //取version_public_checksumBytes后4个字节
        byte[] checkSumBytes = Arrays.copyOfRange(version_public_checksumBytes, len - 4, len);
        //取version_public_checksumBytes前21字节
        byte[] ver = Arrays.copyOfRange(version_public_checksumBytes, 0, len - 4);
        //取ver进行两次sha256哈希运算，取第二次结果值的前4个字节
        byte[] hash1 = StaticMethod.getHash256(ver);
        byte[] hash2 = StaticMethod.getHash256(hash1);
        byte[] checkBytes = Arrays.copyOfRange(hash2, 0, 4);
        //将checkSumBytes与checkBytes比较，如果一致则说明地址有效，返回true
        checkBytes.equals(checkSumBytes);
        return Arrays.equals(checkSumBytes, checkBytes);
    }

}
